# 模拟M300仪器，用udp广播数据
# 参考数据 raw_data\课题5\M300\asc20190924\M300.udp.1029.dat
# 自动变化数据：
# 格式参考 raw_data\课题5\M300\asc20190924\udpout.asc

import parse
import fire
import time
import datetime
import os
import random
import regex as re
from math import sin
import socket
from sys import platform

# 输出log，便于调试
import logging

def get_config(config_file = r'raw_data/课题5/M300/asc20190924/udpout.asc'):
    """从配置文件生成相关格式配置"""
    @parse.with_pattern(r'\"([\w\/\^\(\)\ \.\,%]+)\"')
    def parse_quoted_str(text):
        return text[1:-1]

    display_names = []
    format_strs = []
    col_names = []
    data_types = []
    with open(config_file,mode='r') as config_file:
        for line in config_file.readlines():
            if line.startswith(';'): continue
            line = line.strip()
            if line.strip()=="": continue
            format_str = r'{display_name:quoted} {tag1:w} {tag2:d} {tag3:w} {format:quoted}'
            #print(line)
            res = parse.parse(format_str, line,dict(quoted=parse_quoted_str))
            print(res)
            display_names.append(res.named['display_name'])
            format = res.named['format']
            format_strs.append(format)
            col_names.append(re.sub(r'[^a-zA-Z0-9]','_',res.named['display_name']).strip().lower())
            f = re.findall(r'\%.*?([sfcdge])',format)[0]
            data_types.append(f)
        data_types = dict(zip(col_names,data_types))
    return display_names,format_strs,col_names,data_types

def update_data_dict_gen(t=None,mode='sin',selected_cols=None,data_types=None):
    """假造一些数据，返回字典格式
    """
    now_ts = time.time() if t is None else t # 默认当前时间
    update_data_dict = {}
    
    # 时间处理
    # 时间相关的项有header_and_date,time,seconds_midnight
    now_datetime = datetime.datetime.fromtimestamp(now_ts)
    
    ymd_str = now_datetime.strftime("%Y/%m/%d")
    hms_str = now_datetime.strftime("%H:%M:%S")
    # time in seconds since the start of the day

    # 生成数据
    if mode == 'sin':
        # 正弦变化的数据
        period = 100 # s
        shift = 10 # s
        for i,col in enumerate(selected_cols):
            if col == 'seconds_midnight':
                update_data_dict[col] = 0 # 不知道为啥
            elif data_types[col].lower() in 'feg':
                update_data_dict[col] = 100*(1+sin((now_ts+shift*i)/period)) # 保证经纬度在一定范围内
            elif data_types[col].lower() in 'd': # rice_mso_freq_hz?
                update_data_dict[col] = int(100*(2+sin((now_ts+shift*i)/period)))
            elif data_types[col].lower() in 'c':
                update_data_dict[col] = {'cr':'\r','lf':'\n'}[col]
            elif data_types[col].lower() in 's': # date and time
                update_data_dict[col] = {'header_and_date':ymd_str,'time':hms_str}[col]
            else :
                raise RuntimeError(f'unknown data type {col}:{data_types[col]}')
    elif mode == 'random':
        # 随机数据
        max = 1.0
        min = 0.0
        for i,col in enumerate(selected_cols):
            update_data_dict[col] = random.random()*(max-min)+min
    else:
        raise RuntimeError(f'mode={mode}, it must be "sin" or "random"')
    return update_data_dict

def main(udp_port = 1029,
        udp_broadcast_addr='192.168.31.255', # 本地广播
        delay_time = 0.95,
        config_file = r'raw_data\课题5\M300\asc20190924\udpout.asc',
        max_time_hours = 24,
        mode='sin',
        log_level = 'INFO'):
    """UDP广播输出
    """
    # 初始化logger
    logging.basicConfig(level = getattr(logging,log_level.upper(),logging.INFO)) # 默认info
    file_base_name = os.path.basename(__file__)
    logger = logging.getLogger(file_base_name)

    # 分隔符
    sep = ''
    # 列名  
    display_names,format_strs,col_names,data_types = get_config(config_file)

    # 默认值，为保持完整性
    defaults_str = 'M300,2021/01/24,03:58:08,    0,116.31483, 40.15115,       33,  3.1,   0.0,  87.9,    1.260,      0.0,    270.0,      0.0,39960,  6.5,  0.0,   0.0000,   0.0000,     0.00,     0.00,     0.00,    0.122,    6.098,   -0.020,    0.226,1.54e+009,ENDM300'
    
    # 生成格式化字符串
    write_format_str = ''.join(format_strs)
    logger.info(write_format_str)

    # 这些是会变化的列
    selected_cols = col_names
    
    # 建立socket
    server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    if platform != "win32":
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

    server.settimeout(0.2)

    #data lines
    start_time = time.time()
    while(time.time()<start_time + max_time_hours*24*3600): # max hours
        data_dict = update_data_dict_gen(mode=mode,selected_cols=selected_cols,data_types=data_types)
        logger.info(data_dict)
        value_tuple = tuple( data_dict[col_name] for col_name in col_names)
        #logger.info(value_tuple)
        message = write_format_str.__mod__(value_tuple)
        logger.info(message)
        server.sendto(message.encode('utf-8'),(udp_broadcast_addr,udp_port))
        time.sleep(delay_time)

if __name__ == "__main__":
    fire.Fire(main) # 利用fire命令行化
